﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Practices.ServiceLocation;
using NinjectAdapter;
using Ninject;
using System.Threading;

namespace CCM.CronTaskScheduler.Tests {
    /// <summary>
    /// Summary description for UnitTest1
    /// </summary>
    [TestClass]
    public class UnitTests {
        public UnitTests() {
            //
            // TODO: Add constructor logic here
            //
        }

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext {
            get {
                return testContextInstance;
            }
            set {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        [ClassInitialize()]
        public static void MyClassInitialize(TestContext testContext) {
            IKernel kernel = new StandardKernel(new TestKernel());
            var locator = new NinjectServiceLocator(kernel);
            ServiceLocator.SetLocatorProvider(() => locator);
        }

        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        public void MyTestCleanup() {
            var smgr = ServiceLocator.Current.GetInstance<IScheduleManager>();
            smgr.ClearAllTaskDefinitions();
        }

        #endregion

        [TestMethod]
        public void CanDefineStandardTask() {
            var smgr = ServiceLocator.Current.GetInstance<IScheduleManager>();
            var def = smgr.AddTaskDefinition(GetTestStandardTaskDef());
            Assert.IsTrue(smgr.TaskExists<TestStandardTask1>());
            Assert.IsTrue(smgr.TaskExists(def.TaskName));

        }

        [TestMethod]
        public void CannotDefineDuplicateTask() {
            var smgr = ServiceLocator.Current.GetInstance<IScheduleManager>();
            var def = smgr.AddTaskDefinition(GetTestStandardTaskDef());

            var exceptionOccurred = false;
            try {
                smgr.AddTaskDefinition(GetTestStandardTaskDef());
            } catch (Exception) {
                exceptionOccurred = true;

            }
            Assert.IsTrue(exceptionOccurred);
        }

        private TaskDefinition GetTestStandardTaskDef() {
            var def = new TaskDefinition {
                TaskType = typeof(TestStandardTask1),
                TaskName = "Test Standard Task"
            };
            return def;
        }

        [TestMethod]
        public void CanRunTaskNowInSequence() {
            var smgr = ServiceLocator.Current.GetInstance<IScheduleManager>();
            var def = smgr.AddTaskDefinition(GetTestStandardTaskDef());
            var taskRan = false;
            TestStandardTask1.CallBack = () => {
                taskRan = true;
            };
            smgr.RunNow(def.TaskName);
            while (smgr.HasPendingTasks() || smgr.IsBusy()) {
                Thread.Sleep(1000);
            }
            Assert.IsTrue(taskRan);
        }

        [TestMethod]
        public void CanRunTasksNowInParallel() {
            var task1Ran = false;
            var task2Ran = false;
            TestStandardTask1.CallBack = () => {
                task1Ran = true;
            };
            TestStandardTask2.CallBack = () => {
                task2Ran = true;
            };
            var smgr = ServiceLocator.Current.GetInstance<IScheduleManager>();
            var task1 = smgr.AddTaskDefinition(new TaskDefinition {
                TaskName = "Task1",
                RunMode = TaskRunMode.GlobalParallel,
                TaskType = typeof(TestStandardTask1)
            });
            var task2 = smgr.AddTaskDefinition(new TaskDefinition {
                TaskName = "Task2",
                RunMode = TaskRunMode.GlobalParallel,
                TaskType = typeof(TestStandardTask2)
            });
            smgr.RunNow(task1);
            smgr.RunNow(task2);
            while (smgr.HasPendingTasks() || smgr.IsBusy()) {
                Thread.Sleep(100);
            }
            Assert.IsTrue(task1Ran && task2Ran);
        }

        [TestMethod]
        public void TaskInAnotherGroupShouldNotRun() {
            var task1Ran = false;
            var task2Ran = false;
            TestStandardTask1.CallBack = () => {
                task1Ran = true;
            };
            TestStandardTask2.CallBack = () => {
                task2Ran = true;
            };
            var smgr = ServiceLocator.Current.GetInstance<IScheduleManager>();
            var task1 = smgr.AddTaskDefinition(new TaskDefinition {
                TaskName = "Task1",
                GroupName = "Group1",
                RunMode = TaskRunMode.GroupSequential,
                TaskType = typeof(TestStandardTask1)
            });
            var task2 = smgr.AddTaskDefinition(new TaskDefinition {
                TaskName = "Task2",
                GroupName = "Group2",
                RunMode = TaskRunMode.GroupSequential,
                TaskType = typeof(TestStandardTask2)
            });
            smgr.RunNow(task1);
            while (smgr.HasPendingTasks() || smgr.IsBusy()) {
                Thread.Sleep(100);
            }
            Assert.IsTrue(task1Ran && !task2Ran);
        }

        [TestMethod]
        public void CanScheduleTask() {
            var smgr = ServiceLocator.Current.GetInstance<IScheduleManager>();
            var task1 = smgr.AddTaskDefinition(
                @"00 15 * * *",         // Every day at 1500
                new TaskDefinition {
                    TaskName = "Task1",
                    GroupName = "Group1",
                    RunMode = TaskRunMode.GroupSequential,
                    TaskType = typeof(TestStandardTask1)
                });
            var runTime = smgr.GetNextScheduledTime(DateTime.Now.Date, task1);
            Assert.IsNotNull(runTime);
            Assert.AreEqual(DateTime.Now.Date.AddHours(15), runTime.Value);
        }

        [TestMethod]
        public void CanScheduleDailyAt1843PM() {
            var smgr = ServiceLocator.Current.GetInstance<IScheduleManager>();
            var startTime = DateTime.Now.Date.AddHours(18).AddMinutes(43);
            for (int i = 0; i < 7; i++) {
                var runTime = startTime.AddDays(i);
                DateTime nextScheduledTime = smgr.GetNextScheduledTime(runTime, @"43 18 * * *");
                Assert.AreEqual(runTime, nextScheduledTime);
            }
        }
    }
}
